import random
import redis

from django.shortcuts import render
from django.views.generic.base import View
# django 内部的View类，自定义的类要继承
# 1. CBV(class base view) 有利于代码复用
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import authenticate, login, logout
# authenticate的内置验证方法
# login 内置登录方法，实现session，cookie设置
# 自定义验证类继承的类
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from django.http import HttpResponseRedirect, JsonResponse
# 重定向url类
from django.urls import reverse
# 通过别名反解析地址路径
from apps.users.forms import LoginForm, DynamicLoginForm, DynamicLoginPostForm, \
    RegisterGetForm, RegisterPostForm, UploadImageForm, UserInfoForm, ChangePwdForm, UpdateMobileForm
from apps.utils.YunPian import send_single_sms
from apps.utils.tools import random_string
from online_class.settings import yp_apikey, REDIS_HOST, REDIS_PORT
from apps.users.models import UserProfile
from apps.operations.models import UserCourse, UserFavorite, UserMessage, Banner
from apps.organizations.models import CourseOrg, Teacher
from apps.courses.models import Course
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger


class CustomAuth(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = UserProfile.objects.get(Q(username=username) | Q(mobile=username))
            if user.check_password(password):
                return user
        except Exception as e:
            return None


def message_nums(request):
    """全局中间件"""
    if request.user.is_authenticated:
        return {'unread_nums': request.user.usermessage_set.filter(has_read=False).count()}
    else:
        return {}


class MessageView(LoginRequiredMixin, View):
    """我的消息"""
    login_url = "/login/"

    def get(self, request, *args, **kwargs):
        messages = UserMessage.objects.filter(user=request.user)
        current_page = "message"
        for message in messages:
            message.has_read = True
            message.save()

        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1

        p = Paginator(messages, per_page=1, request=request)
        # 传入筛选后的查询集每页展示多少数据

        messages = p.page(page)
        # 页码包含的对象

        return render(request, "usercenter-message.html", {
            "messages": messages,
            "current_page": current_page
        })


class MyFavCourseView(LoginRequiredMixin, View):
    """我的收藏，教师收藏"""
    login_url = "/login/"

    def get(self, request, *args, **kwargs):
        current_page = "myfavcourse"
        course_list = []
        fav_courses = UserFavorite.objects.filter(user=request.user, fav_type=1)
        for fav_course in fav_courses:
            try:
                # 不是外键，是存储的编号，所以要再筛选一次,拿到课程对象
                course = Course.objects.get(id=fav_course.fav_id)
                # 将课程对象放到列表中
                course_list.append(course)
            except Course.DoesNotExist as e:
                # 如果数据库中没有该课程直接跳过，可能调试的时候动了数据
                pass
        return render(request, "usercenter-fav-course.html", {
            "course_list": course_list,
            "current_page": current_page
        })


class MyFavTeacherView(LoginRequiredMixin, View):
    """我的收藏，教师收藏"""
    login_url = "/login/"

    def get(self, request, *args, **kwargs):
        current_page = "myfavteacher"
        teacher_list = []
        fav_teachers = UserFavorite.objects.filter(user=request.user, fav_type=3)
        for fav_teacher in fav_teachers:
            # 不是外键，是存储的编号，所以要再筛选一次
            teacher = Teacher.objects.get(id=fav_teacher.fav_id)
            teacher_list.append(teacher)
        return render(request, "usercenter-fav-teacher.html", {
            "teacher_list": teacher_list,
            "current_page": current_page
        })


class MyFavOrgView(LoginRequiredMixin, View):
    login_url = "/login/"

    def get(self, request, *args, **kwargs):
        current_page = "myfavorg"
        org_list = []
        fav_orgs = UserFavorite.objects.filter(user=request.user, fav_type=2)
        for fav_org in fav_orgs:
            # 不是外键，是存储的编号，所以要再筛选一次
            org = CourseOrg.objects.get(id=fav_org.fav_id)
            org_list.append(org)
        return render(request, "usercenter-fav-org.html", {
            "org_list": org_list,
            "current_page": current_page
        })


class MyCourseView(LoginRequiredMixin, View):
    """我的课程"""
    login_url = "/login/"

    def get(self, request, *args, **kwargs):
        my_courses = UserCourse.objects.filter(user=request.user)
        # 找到该生的所有课程
        current_page = "mycourse"
        # 记录当前页面的变量
        return render(request, "usercenter-mycourse.html", {
            "my_courses": my_courses,
            "current_page": current_page
        })


class ChangeMobileView(LoginRequiredMixin, View):
    """修改手机号码"""
    login_url = "/login/"

    def post(self, request, *args, **kwargs):
        mobile_form = UpdateMobileForm(request.POST)
        if mobile_form.is_valid():
            mobile = mobile_form.cleaned_data["mobile"]
            # 已经存在的记录不能重复注册
            if request.user.mobile == mobile:
                return JsonResponse({
                    "mobile": "和当前手机号码一致"
                })
            if UserProfile.objects.filter(mobile=mobile):
                return JsonResponse({
                    "mobile": "该手机号码已经被占用"
                })
            user = request.user
            user.mobile = mobile
            user.username = mobile
            user.save()
            return JsonResponse({
                "status": "success"
            })
        else:
            return JsonResponse(mobile_form.errors)


class ChangePwdView(View):
    """修改密码"""

    def post(self, request, *args, **kwargs):
        pwd_form = ChangePwdForm(request.POST)
        if pwd_form.is_valid():
            pwd1 = pwd_form.cleaned_data["password1"]
            # 放在表单验证中完成
            # pwd2 = pwd_form.cleaned_data["password2"]
            #
            # if pwd1 != pwd2:
            #     return JsonResponse({
            #         "status": "fail",
            #         "msg": "密码不一致"
            #     })
            user = request.user
            user.set_password(pwd1)
            user.save()
            # 修改成功，django会自动退出重新登陆
            # 如果不想退出,增加登录
            login(request, user)

            return JsonResponse({
                "status": "success"
            })
        else:
            return JsonResponse(pwd_form.errors)


class UploadImageView(LoginRequiredMixin, View):
    login_url = "/login/"

    # 传统的文件保存存在如下问题 大文件上传
    # def save_file(self, file):
    #     """保存文件"""
    #     with open("E:/code/online_class/media/head_image/uploaded.jpg", "wb") as f:
    #         for chunk in file.chunks():
    #             f.write(chunk)
    #
    # def post(self, request, *args, **kwargs):
    #     # 处理用户上传的头像,文件用request的FILES属性中
    #     files = request.FILES["image"]
    #     self.save_file(files)
    #     # 1. 如果同一个文件上传多次，相同名称的文件该如何处理
    #     # 2. 文件的保存路径应该写入到user
    #     # 3. 还没有做表单验证

    def post(self, request, *args, **kwargs):
        """处理用户上传的头像"""
        image_form = UploadImageForm(request.POST, request.FILES, instance=request.user)
        # instance = request.user 指明修改的是哪个实例,指明是修改行为，request.FILES传入文件类型的request
        if image_form.is_valid():
            image_form.save()
            return JsonResponse({
                "status": "success"
            })
        else:
            return JsonResponse({
                "status": "fail"
            })


class UserInfoView(LoginRequiredMixin, View):
    login_url = "/login/"

    # 显示用户信息
    def get(self, request, *args, **kwargs):
        current_page = "info"
        captcha_form = RegisterGetForm()
        return render(request, "usercenter-info.html", {
            "captcha_form": captcha_form,
            "current_page": current_page
        })

    def post(self, request, *args, **kwargs):
        user_info_form = UserInfoForm(request.POST, instance=request.user)
        # instance = request.user 指明修改的是哪个实例,指明是修改行为
        if user_info_form.is_valid():
            user_info_form.save()
            return JsonResponse({
                "status": "success"
            })
        else:
            return JsonResponse(user_info_form.errors)
            # 返回表单验证的错误


class RegisterView(View):

    def get(self, request, *args, **kwargs):
        register_get_form = RegisterGetForm()
        return render(request, "register.html", {
            "register_get_form": register_get_form
        })

    def post(self, request, *args, **kwargs):
        """图像验证码验证逻辑在SendSmsView的表单验证中实现"""
        register_post_form = RegisterPostForm(request.POST)
        if register_post_form.is_valid():
            mobile = register_post_form.cleaned_data["mobile"]
            password = register_post_form.cleaned_data["password"]
            # 新建一个用户
            user = UserProfile(username=mobile)
            user.set_password(password)
            # 内置方法加密
            user.mobile = mobile
            user.save()
            login(request, user)
            return HttpResponseRedirect(reverse("index"))
        else:
            register_get_form = RegisterGetForm()
            return render(request, "register.html", {
                "register_get_form": register_get_form,
                "register_post_form": register_post_form
            })


class DynamicLoginView(View):
    def get(self, request, *args, **kwargs):
        # login_label = True
        if request.user.is_authenticated:
            # 登录后浏览器根据是否有sessionid来判断是否登录的
            return HttpResponseRedirect(reverse("index"))
        banners = Banner.objects.all()[:3]
        next = request.GET.get("next", "")
        login_form = DynamicLoginForm()
        return render(request, "login.html", {
            "login_form": login_form,
            # "login_label": login_label
            "next": next,
            "banners": banners,
        })

    def post(self, request, *args, **kwargs):
        login_form = DynamicLoginPostForm(request.POST)
        dynamic_login = True
        banners = Banner.objects.all()[:3]
        if login_form.is_valid():
            # 没有注册账号依然可以登录,查询数据库
            mobile = login_form.cleaned_data["mobile"]
            # code = login_form.cleaned_data["code"]
            existed_users = UserProfile.objects.filter(mobile=mobile)
            if existed_users:
                # 查找到用户
                user = existed_users[0]
            else:
                # 新建一个用户
                user = UserProfile(username=mobile)
                password = random_string(10)
                user.set_password(password)
                # 内置方法加密
                user.mobile = mobile
                user.save()
            login(request, user)
            next = request.GET.get("next", "")
            if next:
                return HttpResponseRedirect(next)
            return HttpResponseRedirect(reverse("index"))
        else:
            # 表单验证未通过；重新实例化带图像验证码的类
            # 传递"dynamic_login": dynamic_login，来控制前端显示动态登录界面还是账号密码登录界面
            d_form = DynamicLoginForm()
            return render(request, "login.html", {"d_login_form": login_form,
                                                  "d_form": d_form,
                                                  "dynamic_login": dynamic_login,
                                                  "banners": banners})


class SendSmsView(View):
    """发送手机验证码"""

    def post(self, request, *args, **kwargs):
        send_sms_form = DynamicLoginForm(request.POST)
        re_dict = {}
        # 反馈给前端ajax的信息；组装前端需要的键值对
        if send_sms_form.is_valid():
            mobile = send_sms_form.cleaned_data["mobile"]
            # 随机生成手机数字验证码
            code = random_string()
            res_json = send_single_sms(yp_apikey, code=code, moblie=mobile)
            if res_json["code"] == 0:
                # 短信发送成功
                re_dict["status"] = "success"
                r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password="123456", db=0, charset="utf8",
                                decode_responses=True)
                r.set(str(mobile), code)
                r.expire(str(mobile), 60 * 5)  # 设置验证码5分钟过期
            else:
                re_dict['msg'] = res_json["msg"]
                # 返回错误信息，云片网api自带
            return JsonResponse(re_dict)
        else:
            for key, value in send_sms_form.errors.items():
                # 表单验证未通过返回的错误信息；错误格式{"key":errorlist["错误信息"]}
                re_dict[key] = value[0]

        return JsonResponse(re_dict)


class LogoutView(View):

    def get(self, request, *args, **kwargs):
        logout(request)
        return HttpResponseRedirect(reverse("index"))


class LoginView(View):
    """同时处理get和post"""

    def get(self, request, *args, **kwargs):
        # login_label = True
        if request.user.is_authenticated:
            # 登录后浏览器根据是否有sessionid来判断是否登录的
            return HttpResponseRedirect(reverse("index"))
        banners = Banner.objects.all()[:3]
        next = request.GET.get("next", "")
        login_form = DynamicLoginForm()
        return render(request, "login.html", {
            "login_form": login_form,
            # "login_label": login_label
            "next": next,
            "banners": banners,
        })

    def post(self, request, *args, **kwargs):
        # 有form表单验证了就不需要再自动以两个变量了
        # username = request.POST.get("username", "")
        # password = request.POST.get("password", "")
        # 获取用户名和密码，前端表单的input的name属性为username和password

        # if not username:
        #     return render(request, "login.html", {"msg": "请输入用户名"})
        #
        # if not password:
        #     return render(request, "login.html", {"msg": "请输入密码"})
        #
        # if len(password) < 3:
        #     return render(request, "login.html", {"msg": "密码输入格式不正确"})
        banners = Banner.objects.all()[:3]
        # 表单验证
        login_form = LoginForm(request.POST)

        if login_form.is_valid():
            username = login_form.cleaned_data["username"]
            password = login_form.cleaned_data["password"]
            # username等字段必须要和前端input的name值一致。
            # from apps.users.models import UserProfile
            # 1. 通过用户名查询到用户
            # 2. 密码验证需要先加密再通过加密之后的密码查询
            # user = UserProfile.objects.get(username=username, password=password)
            user = authenticate(username=username, password=password)
            # 研究源码！！！
            # 用于通过用户名和密码查询用户是否存在
            # 验证通过后返回自定义的UserProfile对象
            if user is not None:
                # 查询到用户
                # 研究源码！！
                login(request, user)
                # 登录成功后浏览器得到sessionid，就知道了登录状态
                # 内置方法完成取到session_id放置到cookie
                # 登录成功之后应该怎么返回页面
                next = request.GET.get("next", "")
                if next:
                    return HttpResponseRedirect(next)
                return HttpResponseRedirect(reverse("index"))
                # 重定向
            else:

                # 未查询到用户或
                return render(request, "login.html", {"msg": "用户名或密码错误", "login_form": login_form, "banners": banners})
        else:
            # 表单验证没通过
            return render(request, "login.html", {"login_form": login_form, "banners": banners})
